using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;

using Atomic.Thermodynamics.Electrons;

namespace Atomic.Vasp
{
	/// <summary>
	/// Electronic density of states (DOS) normalized to the total number of electrons per unit cell. Be aware that for relaxations the computed DOS is usually useless.
	/// http://cms.mpi.univie.ac.at/vasp/vasp/DOSCAR_file.html
	/// </summary>
	[Serializable]
	public static class VaspElectronicStateDensity
	{
		/// <summary>
		/// Parses VASP's DOSCAR file.
		/// </summary>
		public static ElectronicStateDensity Parse(double fermiEnergy, double electrons, TextReader reader)
		{
			List<ElectronicStateDensityPoint> points = new List<ElectronicStateDensityPoint>();

			// Skip header lines.
			for (int i = 0; i < 5; i++)
			{
				reader.ReadLine();
			}

			// Except last header line containing higher-accuracy values for energies.
			string line0 = reader.ReadLine().Trim();
			while (line0.Contains("  "))
			{
				line0 = line0.Replace("  ", " ");
			}

			string[] cols0 = line0.Split(' ');

			double emax = double.Parse(cols0[0], CultureInfo.InvariantCulture);
			double emin = double.Parse(cols0[1], CultureInfo.InvariantCulture);
			double n = int.Parse(cols0[2]);

			int j = 0;
			string line;
			while ((line = reader.ReadLine()) != null)
			{
				line = line.Trim();
				while (line.Contains("  "))
				{
					line = line.Replace("  ", " ");
				}

				string[] cols = line.Split(' ');

				if (cols.Length == 3)
				{
					//double e = double.Parse(cols[0], CultureInfo.InvariantCulture);
					double e = emin + (emax - emin) / (n - 1) * j;
					double d = double.Parse(cols[1], CultureInfo.InvariantCulture);
					double s = double.Parse(cols[2], CultureInfo.InvariantCulture);
					points.Add(new ElectronicStateDensityPoint(e, d, s));
				}
				else if (cols.Length == 5)
				{
					//double e = double.Parse(cols[0], CultureInfo.InvariantCulture);
					double e = emin + (emax - emin) / (n - 1) * j;
					double d1 = double.Parse(cols[1], CultureInfo.InvariantCulture);
					double d2 = double.Parse(cols[2], CultureInfo.InvariantCulture);
					double s1 = double.Parse(cols[3], CultureInfo.InvariantCulture);
					double s2 = double.Parse(cols[4], CultureInfo.InvariantCulture);
					points.Add(new ElectronicStateDensitySpinPolarizedPoint(e, d1, d2, s1, s2));
				}
				else
				{
					throw new NotSupportedException();
				}

				j++;
			}

			// First entry is used to determine whether the file contains spin-polarized information.
			if (points.Count > 0 && points[0] is ElectronicStateDensitySpinPolarizedPoint)
			{
				return new ElectronicStateDensity(points.Select(p => (ElectronicStateDensitySpinPolarizedPoint)p), fermiEnergy, electrons);
			}
			else
			{
				return new ElectronicStateDensity(points, fermiEnergy, electrons);
			}
		}
	}
}
